/*
 * Copyright (c) 2023 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "dyn_services_card_channel_scp.h"

#include <securec.h>
#include <stddef.h>

#include "logger.h"

/* GPD_TEE_SE_API_v1.1.1_PublicRelease_CC: 6.2.3 Secure Channel Protocol Support */
#define OID_SCP03_BUFFER "\x06\x08\x2A\x86\x48\x86\xFC\x6B\x04\x03"
#define OID_SCP03_BUFFER_LEN 0x0A

#define AES_KEY_SIZE 32

#define SCP03_KEY_SIZE 16
#define SCP03_KEY_GROUP_SIZE 48

#define BIT_NUM_OF_UINT8 8

TEE_ObjectHandle CreateKeyObject(const uint8_t *key, uint32_t size)
{
    TEE_ObjectHandle object = NULL;
    TEE_Result ret = TEE_AllocateTransientObject(TEE_TYPE_AES, AES_KEY_SIZE * BIT_NUM_OF_UINT8, &object);
    if (ret != TEE_SUCCESS) {
        LOG_ERROR("TEE_AllocateTransientObject error:%x", ret);
        return NULL;
    }

    uint8_t buffer[SCP03_KEY_SIZE] = {0};
    if (memcpy_s(buffer, SCP03_KEY_SIZE, key, size) != EOK) {
        LOG_ERROR("key prepare error");
        TEE_FreeTransientObject(object);
        return NULL;
    }
    TEE_Attribute attribute;
    TEE_InitRefAttribute(&attribute, TEE_ATTR_SECRET_VALUE, buffer, size);

    ret = TEE_PopulateTransientObject(object, &attribute, 1);
    if (ret != TEE_SUCCESS) {
        LOG_ERROR("TEE_PopulateTransientObject error:%x", ret);
        TEE_FreeTransientObject(object);
        return NULL;
    }
    return object;
}

void DeleteKeyObject(TEE_ObjectHandle object)
{
    if (object == NULL) {
        return;
    }
    TEE_FreeTransientObject(object);
}

bool CreateScpKeyObjects(const uint8_t *key, uint32_t keyLen, TEE_ObjectHandle *enc, TEE_ObjectHandle *mac)
{
    if (key == NULL) {
        LOG_ERROR("key is nullptr");
        return false;
    }
    if (keyLen != SCP03_KEY_GROUP_SIZE) {
        LOG_ERROR("keylen %u is not right", keyLen);
        return false;
    }
    if (enc == NULL || mac == NULL) {
        LOG_ERROR("enc or mac is nullptr");
        return false;
    }

    TEE_ObjectHandle encObject = CreateKeyObject(key, SCP03_KEY_SIZE);
    if (encObject == NULL) {
        LOG_ERROR("CreateKeyObject encObject is nullptr");
        return false;
    }

    TEE_ObjectHandle macObject = CreateKeyObject(key + SCP03_KEY_SIZE, SCP03_KEY_SIZE);
    if (macObject == NULL) {
        LOG_ERROR("CreateKeyObject macObject is nullptr");
        DeleteKeyObject(encObject);
        return false;
    }

    *enc = encObject;
    *mac = macObject;
    return true;
}

TEE_SC_Params *CreateScpParams(const uint8_t *key, uint32_t keyLen, uint8_t keyVersion, uint8_t keyId)
{
    if (key == NULL || keyLen == 0) {
        LOG_ERROR("input is nullptr");
        return NULL;
    }
    TEE_SC_Params *params = malloc(sizeof(TEE_SC_Params));
    if (params == NULL) {
        LOG_ERROR("malloc TEE_SC_Params is nullptr");
        return NULL;
    }

    TEE_ObjectHandle enc = NULL;
    TEE_ObjectHandle mac = NULL;
    if (!CreateScpKeyObjects(key, keyLen, &enc, &mac)) {
        free(params);
        return NULL;
    }

    (void)memset_s(params, sizeof(TEE_SC_Params), 0, sizeof(TEE_SC_Params));

    params->scType = TEE_SC_TYPE_SCP03;
    params->scOID.buffer = (uint8_t *)OID_SCP03_BUFFER;
    params->scOID.bufferLen = OID_SCP03_BUFFER_LEN;
    params->scSecurityLevel = TEE_SC_CR_ENC_MAC; /* 0x33 */
    params->scCardKeyRef.scKeyID = keyId;
    params->scCardKeyRef.scKeyVersion = keyVersion;
    params->scDeviceKeyRef.scKeyType = TEE_SC_KEY_SET;
    params->scDeviceKeyRef.__TEE_key.scKeySetRef.scKeyEncHandle = enc;

    params->scDeviceKeyRef.__TEE_key.scKeySetRef.scKeyMacHandle = mac;

    return params;
}

void DeleteScpParams(TEE_SC_Params *params)
{
    if (params == NULL) {
        return;
    }
    DeleteKeyObject(params->scDeviceKeyRef.__TEE_key.scKeySetRef.scKeyEncHandle);
    DeleteKeyObject(params->scDeviceKeyRef.__TEE_key.scKeySetRef.scKeyMacHandle);
    free(params);
}